package org.example2;

import java.lang.reflect.Constructor;
import javassist.*;

public class AppGenProxy {
    /**
     * https://www.cnblogs.com/flyingeagle/articles/7102282.html
     *
     * 生成Station类的代理类StationProxy,并调用其中的方法
     *
     * 添加复杂方法 (有入参和返回值 )必须使用new CtMethod构造方法的方式,不能用CtMethod.make(xx)方式
     *
     */
    public static void main(String[] args) throws Exception {
        Class proxyClazz = createProxyClass();

        //获取构造器
        Constructor constructor = proxyClazz.getConstructor(Station.class);
        //通过构造器实例化
        TicketService  proxyInstance = (TicketService) constructor.newInstance(new Station());

        String inquireReturn = proxyInstance.inquire("G3121");
        System.out.println(inquireReturn);

        System.out.println();
        proxyInstance.sellTicket();

        System.out.println();
        proxyInstance.withdraw();
    }

    /**
     * 手动创建字节码
     */
    private static Class createProxyClass() throws Exception {

        //设置将生成的class放置的目录
        String postFix = "target/genClassByJavassist2";
        String genClassDir = System.getProperty("user.dir") +"/javassistTest/"+postFix ;
        System.out.println("生成的字节码将放在"+postFix+"目录中");
        System.out.println();

        ClassPool classPool = ClassPool.getDefault();
        CtClass ctClass = classPool.makeClass("org.example2.StationProxy");

        //设置接口  
        CtClass interface1 = classPool.get("org.example2.TicketService");
        ctClass.setInterfaces(new CtClass[] { interface1 });

        //设置Field  
        CtField field = CtField.make("private org.example2.Station station;", ctClass);
        ctClass.addField(field);

        CtClass stationClass = classPool.get("org.example2.Station");
        CtClass[] paramArray = new CtClass[] { stationClass };
        CtConstructor ctConstructor = CtNewConstructor.make(paramArray, null,
                CtNewConstructor.PASS_NONE, null, null, ctClass);
        //设置构造函数内部信息  
        ctConstructor.setBody("{this.station=$1;}");
        ctClass.addConstructor(ctConstructor);

        //创建收取手续 takeHandlingFee方法  
        CtMethod takeHandlingFee = CtMethod.make("private void takeHandlingFee() {}", ctClass);
        takeHandlingFee.setBody("System.out.println(\"收取手续费,打印发票.....\");");
        ctClass.addMethod(takeHandlingFee);

        //创建showAlertInfo方法  
        CtMethod showInfo = CtMethod.make("private void showAlertInfo(String info) {}", ctClass);
        showInfo.setBody("System.out.println($1);");
        ctClass.addMethod(showInfo);

        //sellTicket  
        CtMethod sellTicket = CtMethod.make("public void sellTicket(){}", ctClass);
        StringBuilder sellTicketBuilder = new StringBuilder();
        sellTicketBuilder.append("{\nthis.showAlertInfo(\"××××欢迎光临本代售点--购票××××\");\n");
        sellTicketBuilder.append("station.sellTicket();\n");
        sellTicketBuilder.append("this.takeHandlingFee(); \n } ");
        sellTicket.setBody(sellTicketBuilder.toString());
        ctClass.addMethod(sellTicket);

        //添加inquire方法 注意添加复杂方法(有入参和返回值)不能使用这种方式
//        CtMethod inquire = CtMethod.make("public String inquire(String msg){}", ctClass);
//        StringBuilder inquireBuilder = new StringBuilder();
//        inquireBuilder.append("{\nthis.showAlertInfo(\"××××欢迎光临本代售点××××\");\n");
//        inquireBuilder.append("String result=this.station.inquire($1);\n");
//        inquireBuilder.append("this.showAlertInfo(\"××××欢迎您的光临,再见!××××\"); \n");
//        inquireBuilder.append("return  \"代理类--\" + result; \n}");
//        ctClass.addMethod(inquire);

        // 添加复杂方法(有入参和返回值)必须使用new CtMethod构造方法的方式,不能用CtMethod.make(xx)方式
        CtMethod inquireMethod = new CtMethod(classPool.getCtClass("java.lang.String"), "inquire",
                new CtClass[]{classPool.getCtClass("java.lang.String")}, ctClass);
        inquireMethod.setModifiers(Modifier.PUBLIC);
        StringBuilder inquireMethodBuilder = new StringBuilder();
        inquireMethodBuilder.append("{\nthis.showAlertInfo(\"××××欢迎光临本代售点--查询××××\");\n");
        inquireMethodBuilder.append("String result=this.station.inquire($1);\n");
        inquireMethodBuilder.append("return  \"代理类返回值--\" + result; \n}");
        inquireMethod.setBody(inquireMethodBuilder.toString());
        ctClass.addMethod(inquireMethod);


        //添加withdraw方法
        CtMethod withdraw = CtMethod.make("public void withdraw() {}", ctClass);
        StringBuilder withdrawBuilder = new StringBuilder();
        withdrawBuilder.append("{\nthis.showAlertInfo(\"××××欢迎光临本代售点---取票××××\");\n");
        withdrawBuilder.append("station.withdraw();\n");
        withdrawBuilder.append("this.takeHandlingFee();\n}");
        withdraw.setBody(withdrawBuilder.toString());
        ctClass.addMethod(withdraw);

        // 将生成的class信息写入
        ctClass.writeFile(genClassDir);

        return ctClass.toClass();

    }
}